/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package simhya.dataprocessing;

import org.apache.commons.math.ode.DerivativeException;
import org.apache.commons.math.ode.sampling.StepInterpolator;
import simhya.model.flat.FlatModel;
import org.apache.commons.math.ode.sampling.StepHandler;
import org.apache.commons.math.ode.sampling.StepInterpolator;
import org.apache.commons.math.ode.DerivativeException;

/**
 *
 * @author luca
 */
public class OdeDataCollector extends DataCollector implements StepHandler {
    private StepInterpolator interpolator;
    private long steps;

    public OdeDataCollector(FlatModel model) {
        super(model);
        steps = 0;
    }

    public void handleStep(StepInterpolator interpolator, boolean isLast) throws DerivativeException {
        this.interpolator = interpolator;
        steps++;
        double time = interpolator.getCurrentTime();
        if (isLast) 
            putFinalStateOde(time);
        else if (dataNeeded(time))
            putDataOde(time);
    }

    @Override
    public boolean dataNeeded(double nextTime) {
        if (this.saveOnlyFinalState)
            return false;
        if (this.saveByStep && steps == this.lastPrintStep + this.deltaStep)
            return true;
        else if (nextTime > this.lastPrintTime + this.deltaTime)
            return true;
        else return false;
    }

    @Override
    public boolean dataNeeded(double nextTime, long stepNumber) {
        return dataNeeded(nextTime);
    }


    /**
     * stores data at intermediate time points
     * @param nextTime
     * @throws DerivativeException
     */
    void putDataOde(double nextTime) throws DerivativeException {
        if (this.saveByStep) {
            interpolator.setInterpolatedTime(nextTime);
            double [] x = interpolator.getInterpolatedState();
            store.setAllVariableValues(x);
            currentTrajectory.add(nextTime, x, store.getExpressionVariablesValues());
            this.lastPrintStep += this.deltaStep;
        }
        else
            while (lastPrintTime + deltaTime < nextTime) {
                lastPrintTime += deltaTime;
                interpolator.setInterpolatedTime(lastPrintTime);
                double [] x = interpolator.getInterpolatedState();
                store.setAllVariableValues(x);
                currentTrajectory.add(lastPrintTime, x, store.getExpressionVariablesValues());
            }
    }


    /**
     * stores the final state and all intermediate points needed.
     * @param time the final
     * @throws DerivativeException
     */
    void putFinalStateOde(double time) throws DerivativeException {
        double [] x;
        if (this.saveByStep || this.saveOnlyFinalState) {
            interpolator.setInterpolatedTime(time);
            x = interpolator.getInterpolatedState();
            store.setAllVariableValues(x);
            this.currentTrajectory.addFinalState(time, x, store.getExpressionVariablesValues());
            this.currentTrajectory.totalSteps = steps;
        }
        else {
            if (finalTime > this.initialSavingTime) {
                while (this.lastPrintTime + this.deltaTime <= finalTime) {
                    lastPrintTime += deltaTime;
                    if (lastPrintTime <= time) {
                        interpolator.setInterpolatedTime(lastPrintTime);
                        x = interpolator.getInterpolatedState();
                        store.setAllVariableValues(x);
                    } else {
                        interpolator.setInterpolatedTime(time);
                        x = interpolator.getInterpolatedState();
                        store.setAllVariableValues(x);
                    }
                    currentTrajectory.add(lastPrintTime, x, store.getExpressionVariablesValues());
                }
            }
            interpolator.setInterpolatedTime(time);
            x = interpolator.getInterpolatedState();
            store.setAllVariableValues(x);
            this.currentTrajectory.addFinalState(time, x, store.getExpressionVariablesValues());
            this.currentTrajectory.totalSteps = steps;
        }
        this.currentTrajectory.addFinalState(time, x, store.getExpressionVariablesValues());
    }




    public boolean requiresDenseOutput() {
        return true;
    }

    public void reset() {
        super.clearAllTrajectories();
        super.newTrajectory();
    }

    

    

}
